/**
 * Copyright 2010 Adam Ruggles.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package saig.engine.resources;

import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import saig.engine.models.Sprite;


/**
 * Loads all Image Resources for the game.
 * @author Adam
 * @version $Id$
 * 
 * Created on Aug 8, 2010 at 10:23:19 PM 
 */
public final class ImageLoader {
	/**
	 * Logger.
	 */
	private final Logger logger = LoggerFactory.getLogger(ImageLoader.class);
	/**
	 * Base image path.
	 */
	public static final String BASE_IMAGE_PATH = "images/";
	/**
	 * The player reference string.
	 */
	public static final String PLAYER_FRAMES = "player";
	/**
	 * The instance of this image loader.
	 */
	private static final ImageLoader INSTANCE = new ImageLoader();
	/**
	 * The graphics configuration.
	 */
	private GraphicsConfiguration gc;
	/**
	 * Private Constructor.
	 */
	private ImageLoader() {
		gc = GraphicsEnvironment.getLocalGraphicsEnvironment()
			.getDefaultScreenDevice().getDefaultConfiguration();
		loadPlayerSprites();
	}
	/**
	 * Returns an instance of the image loader.
	 * @return an instance of the image loader.
	 */
	public static ImageLoader getInstance() {
		return INSTANCE;
	}
	/**
	 * A Map for storing sprites.
	 */
	private Map<String, Sprite[]> spriteMap = new HashMap<String, Sprite[]>();
	/**
	 * A Map for storing images.
	 */
	private Map<String, Image[]> imageMap = new HashMap<String, Image[]>();
	/**
	 * Returns an image by a reference value.
	 * @param ref The reference value (image file name).
	 * @return An image.
	 */
	private BufferedImage getImage(final String ref) {
		// Load the image.
		BufferedImage srcImage = null;
		try {
			URL url = this.getClass().getClassLoader().getResource(BASE_IMAGE_PATH + ref);
			if (url != null) {
				srcImage = ImageIO.read(url);
			} else {
				logger.error("Image Not found for reference [{}]", ref);
			}
		} catch (IOException e) {
			logger.error("IOException occurred reading image.", e);
		}
		BufferedImage img = null;
		if (srcImage != null) {
			final int transparency = srcImage.getColorModel().getTransparency();
			img = gc.createCompatibleImage(srcImage.getWidth(), srcImage.getHeight(), transparency);
			final Graphics2D g = (Graphics2D) img.getGraphics();
			g.drawImage(srcImage,0,0,null);
			g.dispose();
		}
		return img;
	}
	/**
	 * Returns an array of images by a reference and size.
	 * @param ref The reference value (image file name).
	 * @param width The width of each sub-image.
	 * @param height The height of each sub-image.
	 * @return An array of images by a reference and size.
	 */
	public Image[] getImages(final String ref, final int width, final int height) {
		Image[] images = imageMap.get(ref);
		if (images == null) {
			logger.debug("Loading {} with width {} and height {}.", new Object[] {ref, width, height});
			BufferedImage strip = getImage(ref);
			final int transparency = strip.getColorModel().getTransparency();
			final int cols = strip.getWidth() / width;
			final int rows = strip.getHeight() / height;
			images = new Image[cols * rows];
			logger.debug("Creating {} images", images.length);
			int count = 0;
			for (int y = 0; y < rows; y++) {
				for (int x = 0; x < cols; x++) {
					final Image image = gc.createCompatibleImage(width, height, transparency);
					final Graphics2D g = (Graphics2D) image.getGraphics();
					g.drawImage(strip, -x * width, -y * height, null);
					g.dispose();
					images[count++] = image;
				}
			}
			imageMap.put(ref, images);
		}
		return images;
	}
	/**
	 * Returns an array of sprites for a reference value.
	 * @param ref The reference value to retrieve the frames.
	 * @param width The width of each sprite in the reference file (only used if the reference hasn't been loaded).
	 * @param height The height of each sprite in the reference file (only used if the reference hasn't been loaded).
	 * @return An array of frames for a reference value.
	 */
	public Sprite[] getSprites(final String ref, final int width, final int height) {
		Sprite[] sprites = spriteMap.get(ref);
		if (sprites == null) {
			logger.debug("Loading {} with width {} and height {}.", new Object[] {ref, width, height});
			BufferedImage strip = getImage(ref);
			final int transparency = strip.getColorModel().getTransparency();
			final int cols = strip.getWidth() / width;
			final int rows = strip.getHeight() / height;
			sprites = new Sprite[cols * rows];
			logger.debug("Creating {} sprites", sprites.length);
			int count = 0;
			for (int y = 0; y < rows; y++) {
				for (int x = 0; x < cols; x++) {
					final Image image = gc.createCompatibleImage(width, height, transparency);
					final Graphics2D g = (Graphics2D) image.getGraphics();
					g.drawImage(strip, -x * width, -y * height, null);
					g.dispose();
					sprites[count++] = new Sprite(image);
				}
			}
			spriteMap.put(ref, sprites);
		}
		return sprites;
	}
	/**
	 * Loads the sprite animation for the player.
	 */
	private void loadPlayerSprites() {
		BufferedImage strip = getImage("mario.png");
		final int transparency = strip.getColorModel().getTransparency();
		Sprite[] sprites = new Sprite[56];
		int count = 0;
		for (int y = 0; y < 3; y++) {
			for (int x = 0; x < 15; x++) {
				final BufferedImage spriteImage = gc.createCompatibleImage(32, 32, transparency);
				final Graphics2D g = (Graphics2D) spriteImage.getGraphics();
				g.drawImage(strip, -x * 32, -y * 32, null);
				g.dispose();
				sprites[count++] = new Sprite(spriteImage);
			}
		}
		// Load Small Player images.
		for (int x = 0; x < 11; x++) {
			final BufferedImage spriteImage = gc.createCompatibleImage(16, 16, transparency);
			final Graphics2D g = (Graphics2D) spriteImage.getGraphics();
			g.drawImage(strip, -x * 16, -96, null);
			g.dispose();
			sprites[count++] = new Sprite(spriteImage);
		}
		spriteMap.put(PLAYER_FRAMES, sprites);
	}
}
